/**
  ******************************************************************************
  * @file    state_machine.c
  * @author  Motor Control SDK Team, ST Microelectronics
  * @brief   This file provides firmware functions that implement the features
  *          of the Motor Control State Machine component of the Motor Control SDK:
  *
  *           * Check that transition from one state to another is legal
  *           * Handle the fault processing
  *           * Provide accessor to State machine internal state
  *           * Provide accessor to error state
  *
  ******************************************************************************
  * @attention
  *
  * <h2><center>&copy; Copyright (c) 2019 STMicroelectronics.
  * All rights reserved.</center></h2>
  *
  * This software component is licensed by ST under Ultimate Liberty license
  * SLA0044, the "License"; You may not use this file except in compliance with
  * the License. You may obtain a copy of the License at:
  *                             www.st.com/SLA0044
  *
  ******************************************************************************
  */

/* Includes ------------------------------------------------------------------*/
#include "state_machine.h"

/** @addtogroup MCSDK
  * @{
  */

/** @defgroup STATE_MACHINE Motor Control State Machine
  * @brief Motor Control State Machine component of the Motor Control SDK
  *
  * @todo Document the Motor Control State Machine "module".
  *
  * @{
  */

/**
  * @brief  Initializes all the object variables, usually it has to be called
  *         once right after object creation.
  * @param pHandle pointer on the component instance to initialize.
  * @retval none.
  */
__weak void STM_Init( STM_Handle_t * pHandle )
{

    pHandle->bState = IDLE;
    pHandle->hFaultNow = MC_NO_FAULTS;
    pHandle->hFaultOccurred = MC_NO_FAULTS;
}

/**
  * @brief It submits the request for moving the state machine into the state
  *        specified by bState (FAULT_NOW and FAUL_OVER are not handled by this
  *        method). Accordingly with the current state, the command is really
  *        executed (state machine set to bState) or discarded (no state
  *        changes).
  *        If requested state can't be reached the return value is false and the
  *        MC_SW_ERROR is raised, but if requested state is IDLE_START,
  *        IDLE_ALIGNMENT or ANY_STOP, that corresponds with the user actions:
  *        Start Motor, Encoder Alignemnt and Stop Motor, the MC_SW_ERROR is
  *        not raised.
  * @param pHanlde pointer of type  STM_Handle_t.
  * @param bState New requested state
  * @retval bool It returns true if the state has been really set equal to
  *         bState, false if the requested state can't be reached
  */
__weak bool STM_NextState( STM_Handle_t * pHandle, State_t bState )
{
    bool bChangeState = false;
    State_t bCurrentState = pHandle->bState;
    State_t bNewState = bCurrentState;

    switch ( bCurrentState )
    {
    case ICLWAIT:
        if ( bState == IDLE )
        {
            bNewState = bState;
            bChangeState = true;
        }
        break;
    case IDLE:
        if ( ( bState == IDLE_START ) || ( bState == IDLE_ALIGNMENT )
                || ( bState == ICLWAIT ) )
        {
            bNewState = bState;
            bChangeState = true;
        }
        break;

    case IDLE_ALIGNMENT:
        if ( ( bState == ANY_STOP ) || ( bState == ALIGN_CHARGE_BOOT_CAP )
                || ( bState == ALIGN_OFFSET_CALIB ) )
        {
            bNewState = bState;
            bChangeState = true;
        }
        break;

    case ALIGN_CHARGE_BOOT_CAP:
        if ( ( bState == ALIGN_OFFSET_CALIB ) || ( bState == ANY_STOP ) )
        {
            bNewState = bState;
            bChangeState = true;
        }
        break;

    case ALIGN_OFFSET_CALIB:
        if ( ( bState == ALIGN_CLEAR ) || ( bState == ANY_STOP ) )
        {
            bNewState = bState;
            bChangeState = true;
        }
        break;

    case ALIGN_CLEAR:
        if ( ( bState == ALIGNMENT ) || ( bState == ANY_STOP ) )
        {
            bNewState = bState;
            bChangeState = true;
        }
        break;

    case ALIGNMENT:
        if ( bState == ANY_STOP )
        {
            bNewState = bState;
            bChangeState = true;
        }
        break;

    case IDLE_START:
        if ( ( bState == ANY_STOP ) || ( bState == CHARGE_BOOT_CAP ) ||
                ( bState == START ) ||
                ( bState == OFFSET_CALIB ) || ( bState == IDLE_ALIGNMENT ) )
        {
            bNewState = bState;
            bChangeState = true;
        }
        break;

    case CHARGE_BOOT_CAP:
        if ( ( bState == OFFSET_CALIB ) || ( bState == ANY_STOP ) )
        {
            bNewState = bState;
            bChangeState = true;
        }
        break;

    case OFFSET_CALIB:
        if ( ( bState == CLEAR ) || ( bState == ANY_STOP ) || ( bState == WAIT_STOP_MOTOR ) )
        {
            bNewState = bState;
            bChangeState = true;
        }
        break;

    case WAIT_STOP_MOTOR:
        if ( ( bState == CLEAR ) || ( bState == ANY_STOP ) )
        {
            bNewState = bState;
            bChangeState = true;
        }
        break;

    case CLEAR:
        if ( ( bState == START ) || ( bState == ANY_STOP ) )
        {
            bNewState = bState;
            bChangeState = true;
        }
        break;

    case START:
        if ( ( bState == SWITCH_OVER ) || ( bState == ANY_STOP ) || (bState == START_RUN) )
        {
            bNewState = bState;
            bChangeState = true;
        }
        break;

    case SWITCH_OVER:
        if ( ( bState == START ) || ( bState == ANY_STOP ) || (bState == START_RUN) )
        {
            bNewState = bState;
            bChangeState = true;
        }
        break;

    case START_RUN:
        if ( ( bState == RUN ) || ( bState == ANY_STOP ) )
        {
            bNewState = bState;
            bChangeState = true;
        }
        break;

    case RUN:
        if ( bState == ANY_STOP )
        {
            bNewState = bState;
            bChangeState = true;
        }
        break;

    case ANY_STOP:
        if ( bState == STOP )
        {
            bNewState = bState;
            bChangeState = true;
        }
        break;

    case STOP:
        if ( bState == STOP_IDLE )
        {
            bNewState = bState;
            bChangeState = true;
        }
        break;

    case STOP_IDLE:
        if ( ( bState == IDLE ) || ( bState == ICLWAIT ) )
        {
            bNewState = bState;
            bChangeState = true;
        }
        break;
    default:
        break;
    }

    if ( bChangeState )
    {
        pHandle->bState = bNewState;
    }
    else
    {
        if ( !( ( bState == IDLE_START ) || ( bState == IDLE_ALIGNMENT )
                || ( bState == ANY_STOP ) ) )
        {
            /* If new state is not a user command START/STOP raise a software error */
            STM_FaultProcessing( pHandle, MC_SW_ERROR, 0u );
        }
    }

    return ( bChangeState );
}

/**
  * @brief It clocks both HW and SW faults processing and update the state
  *        machine accordingly with hSetErrors, hResetErrors and present state.
  *        Refer to State_t description for more information about fault states.
  * @param pHanlde pointer of type  STM_Handle_t
  * @param hSetErrors Bit field reporting faults currently present
  * @param hResetErrors Bit field reporting faults to be cleared
  * @retval State_t New state machine state after fault processing
  */
__weak State_t STM_FaultProcessing( STM_Handle_t * pHandle, uint16_t hSetErrors, uint16_t
                                    hResetErrors )
{
    State_t LocalState =  pHandle->bState;

    /* Set current errors */
    pHandle->hFaultNow = ( pHandle->hFaultNow | hSetErrors ) & ( ~hResetErrors );
    pHandle->hFaultOccurred |= hSetErrors;

    if ( LocalState == FAULT_NOW )
    {
        if ( pHandle->hFaultNow == MC_NO_FAULTS )
        {
            pHandle->bState = FAULT_OVER;
            LocalState = FAULT_OVER;
        }
    }
    else
    {
        if ( pHandle->hFaultNow != MC_NO_FAULTS )
        {
            pHandle->bState = FAULT_NOW;
            LocalState = FAULT_NOW;
        }
    }

    return ( LocalState );
}

#if defined (CCMRAM)
#if defined (__ICCARM__)
#pragma location = ".ccmram"
#elif defined (__CC_ARM) || defined(__GNUC__)
__attribute__( ( section ( ".ccmram" ) ) )
#endif
#endif
/**
  * @brief  Returns the current state machine state
  * @param  pHanlde pointer of type  STM_Handle_t
  * @retval State_t Current state machine state
  */
__weak State_t STM_GetState( STM_Handle_t * pHandle )
{
    return ( pHandle->bState );
}


/**
  * @brief It reports to the state machine that the fault state has been
  *        acknowledged by the user. If the state machine is in FAULT_OVER state
  *        then it is moved into STOP_IDLE and the bit field variable containing
  *        information about the faults historically occured is cleared.
  *        The method call is discarded if the state machine is not in FAULT_OVER
  * @param pHanlde pointer of type  STM_Handle_t
  * @retval bool true if the state machine has been moved to IDLE, false if the
  *        method call had no effects
  */
__weak bool STM_FaultAcknowledged( STM_Handle_t * pHandle )
{
    bool bToBeReturned = false;

    if ( pHandle->bState == FAULT_OVER )
    {
        pHandle->bState = STOP_IDLE;
        pHandle->hFaultOccurred = MC_NO_FAULTS;
        bToBeReturned = true;
    }

    return ( bToBeReturned );
}


/**
  * @brief It returns two 16 bit fields containing information about both faults
  *        currently present and faults historically occurred since the state
  *        machine has been moved into state
  * @param pHanlde pointer of type  STM_Handle_t.
  * @retval uint32_t  Two 16 bit fields: in the most significant half are stored
  *         the information about currently present faults. In the least
  *         significant half are stored the information about the faults
  *         historically occurred since the state machine has been moved into
  *         FAULT_NOW state
  */
__weak uint32_t STM_GetFaultState( STM_Handle_t * pHandle )
{
    uint32_t LocalFaultState;

    LocalFaultState = ( uint32_t )( pHandle->hFaultOccurred );
    LocalFaultState |= ( uint32_t )( pHandle->hFaultNow ) << 16;

    return LocalFaultState;
}

/**
  * @}
  */

/**
  * @}
  */

/******************* (C) COPYRIGHT 2019 STMicroelectronics *****END OF FILE****/
